//define the utility for dynamoDB
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, PutCommand, GetCommand } from "@aws-sdk/lib-dynamodb";
import { verifyMessage } from "viem";
import jwt from "jsonwebtoken";

// 初始化 DynamoDB 客户端
const client = new DynamoDBClient({
    region: "us-east-1",
    credentials: {
        accessKeyId: process.env.AWS_USER_ACCESS_KEY_ID || "",
        secretAccessKey: process.env.AWS_USER_ACCESS_KEY || "",
    },
});
const docClient = DynamoDBDocumentClient.from(client);

// 表名
const TABLE_NAME = "blackjack";

// 写入数据到 DynamoDB
async function writeScore(player: string, score: number): Promise<void> {
    const params = {
        TableName: TABLE_NAME,
        Item: {
            player: player,
            score: score,
        },
    };
    try {
        await docClient.send(new PutCommand(params));
        console.log(`Successfully write score ${score} for player ${player}`);
    } catch (error) {
        console.error(`Error writing score to DynamoDB: ${error}`);
        throw error;
    }
}

// 从 DynamoDB 读取数据
async function readScore(player: string): Promise<number | null> {
    const params = {
        TableName: TABLE_NAME,
        Key: {
            player: player,
        },
    };
    try {
        const data = await docClient.send(new GetCommand(params));
        if (data.Item) {
            console.log(`Score for player ${player}: ${data.Item.score}`);
            return data.Item.score as number;
        } else {
            console.log(`No score found for player ${player}`);
            return null;
        }
    } catch (error) {
        console.error(`Error reading score from DynamoDB: ${error}`);
        throw error;
    }
}



export interface Card {
    rank: string;
    suit: string;
}

const ranks = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"];
const suits = ["♠️", "♥️", "♦️", "♣️"];
const initialDeck = ranks.map(rank => suits.map(suit => ({"rank": rank, "suit": suit }))).flat();

const gameState: {
    playerHand: Card[],
    dealerHand: Card[],
    deck: Card[],
    message: string,
    score: number
} = {
    playerHand: [],
    dealerHand: [],
    deck: initialDeck,
    message: "",
    score: 0
}

function getRandomCard(deck: Card[], count: number) {
    const randomIndexSet = new Set<number>();
    while (randomIndexSet.size < count) {
        randomIndexSet.add(Math.floor(Math.random() * deck.length));
    }
    const randomCards = deck.filter((_, index) => randomIndexSet.has(index));
    const remainingDeck = deck.filter((_, index) => !randomIndexSet.has(index));
    return [ randomCards, remainingDeck ];
}

export async function GET(request: Request) {
    const url = new URL(request.url);
    const address = url.searchParams.get("address");
    if (!address) {
        return new Response(JSON.stringify({message: "address is required"}), {status: 400});
    }

    //reset the game state
    gameState.playerHand = [];
    gameState.dealerHand = [];
    gameState.deck = initialDeck;
    gameState.message = "";

    const [playerHand, remainingDeck] = getRandomCard(gameState.deck, 2);
    const [dealerHand, newDeck] = getRandomCard(remainingDeck, 2);
    gameState.playerHand = playerHand;
    gameState.dealerHand = dealerHand;
    gameState.deck = newDeck;
    gameState.message = "";
    
    try {
        const data = await readScore(address);
        if (!data) {
            gameState.score = 0;
        } else {
            gameState.score = data;
        }
    } catch (error) {
        console.error(`Error reading score from DynamoDB: ${error}`);
        return new Response(JSON.stringify({message: "Error reading score from DynamoDB"}), {status: 500});
    }

    return new Response(JSON.stringify({
        playerHand: gameState.playerHand,
        dealerHand: [gameState.dealerHand[0], {rank: "?", suit: "?"} as Card],
        message: gameState.message,
        score: gameState.score
    }), {
        status: 200
    });
}

export async function POST(request: Request) {
    const body = await request.json();
    const { action, address } = body;

    //sign and create token
    if (action === "auth") {
        const { address, message, signature } = body;
        const isValid = verifyMessage({address, message, signature});
        if (!isValid) {
            return new Response(JSON.stringify({message: "Invalid signature"}), {status: 400});
        } else {
            const token = jwt.sign({ address }, process.env.JWT_SECRET || "", { expiresIn: "1h" });
            return new Response(JSON.stringify({
                message: "Valid signature",
                jsonwebtoken: token,
            }), {status: 200});
        }
    }

    //verify token
    const token = request.headers.get("bearer")?.split(" ")[1];
    if (!token) {
        return new Response(JSON.stringify({message: "Token is required"}), {status: 401});
    }
    const decoded = jwt.verify(token, process.env.JWT_SECRET || "") as { address: string };
    if (decoded.address.toLocaleLowerCase() !== address.toLocaleLowerCase()) {
        return new Response(JSON.stringify({message: "Invalid token"}), {status: 401}); 
    }


    if (action === "hit") {
        const [cards, newDeck] = getRandomCard(gameState.deck, 1)
        gameState.playerHand.push(...cards);
        gameState.deck = newDeck;

        const playerHandValue = calculateHandValue(gameState.playerHand);
        if (playerHandValue === 21) {
            gameState.message = "Black Jack! Player wins!";
            gameState.score += 100;
        } else if (playerHandValue > 21) {
            gameState.message = "Bust!, Player loses!";
            gameState.score -= 100;
        }
    } else if (action === "stand") {
        while (calculateHandValue(gameState.dealerHand) < 17) {
            const [cards, newDeck] = getRandomCard(gameState.deck, 1)
            gameState.dealerHand.push(...cards);
            gameState.deck = newDeck;
        }
        const dealerHandValue = calculateHandValue(gameState.dealerHand);
        if (dealerHandValue > 21) {
            gameState.message = "Dealer bust! Player wins!";
            gameState.score += 100;
        } else if (dealerHandValue === 21) {
            gameState.message = "Dealer Black Jack! Player loses!";
            gameState.score -= 100;
        } else {
            const playerHandValue = calculateHandValue(gameState.playerHand);
            if (playerHandValue > dealerHandValue) {
                gameState.message = "Player wins!";
                gameState.score += 100;
            } else if (playerHandValue < dealerHandValue) {
                gameState.message = "Player loses!";
                gameState.score -= 100;
            } else {
                gameState.message = "Draw!";
            }
        }
    } else {
        return new Response(JSON.stringify({message: "Invalid action"}), {status: 400});
    }

    try {
        await writeScore(address, gameState.score);
    } catch (error) {
        console.error(`Error writing score to DynamoDB: ${error}`);
        return new Response(JSON.stringify({message: "Error writing score to DynamoDB"}), {status: 500});
    }

    return new Response(JSON.stringify({
        playerHand: gameState.playerHand,
        dealerHand: gameState.message == "" ? [gameState.dealerHand[0], {rank: "?", suit: "?"} as Card] : gameState.dealerHand,
        message: gameState.message,
        score: gameState.score
    }), {
        status: 200
    });
}

function calculateHandValue(hand: Card[]) {
    let value = 0;
    let aceCount = 0;
    hand.forEach(card => {
        if (card.rank === "A") {
            aceCount++;
            value += 11;
        } else if (["J", "Q", "K"].includes(card.rank)) {
            value += 10;
        } else {
            value += parseInt(card.rank);
        }
    })
    while (value > 21 && aceCount > 0) {
        value -= 10;
        aceCount--;
    }
    return value;
}